﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MyUtils;

namespace MyDebugHelper {
	public static class TestHelper {
		public static void CheckThreadSafity(this Action func) {
			CheckThreadSafity(func, 5, 5);
		}

		/// <summary>
		/// Run N threads simultaniusly, wait for finishing, and rethrow exceptions.
		/// Very usefull for thread safe testing.
		/// </summary>
		/// <param name="func">function to execute</param>
		/// <param name="threadCount">threads count</param>
		/// <param name="experimentsCount">run count per each test</param>
		public static void CheckThreadSafity(this Action func, int threadCount, int experimentsCount) {
			for (int k = 0; k < experimentsCount; k++) {
				Exception lastThreadException = null;
				var threads = new List<Thread>();

				var threadBody = new ThreadStart(delegate {
					try {
						func.Invoke();
					} catch (Exception ex) {
						lastThreadException = ex;
					} finally {
						lock (threads) {
							threads.Remove(Thread.CurrentThread);
						}
					}
				});

				for (int i = 0; i < threadCount; i++) {
					var thread = new Thread(threadBody) {
						IsBackground = true,
						Name = string.Format("ThreadSafeTester #{0} Experiment {1}", i + 1, k + 1),
					};
					lock (threads) {
						threads.Add(thread);
					}
					thread.Start();
				}
				while (threads.Exists()) // actively waiting for threads
					Thread.Sleep(100);

				if (lastThreadException != null) {
					throw new Exception(
						"\r\n\r\nTHREAD UNSAFE (Experiment #" + (k + 1) + " with " + threadCount +
							" threads). One of threads throws exception\r\n", lastThreadException);
				}
			}
		}
	}
}